package cartagows.wsframework.wscoordination;

import org.apache.axiom.om.OMElement;
import org.apache.axis2.addressing.EndpointReference;

import cartago.ArtifactId;
import cartago.CartagoException;
import cartago.Op;
import cartago.OpFeedbackParam;
import cartago.OpRequestTimeoutException;
import cartagows.CartagowsParam;
import cartagows.WSMsgInfo;
import cartagows.util.WSAgent;
import cartagows.util.XMLib;
import cartagows.wsframework.core.DataContainer;
import cartagows.wsframework.core.IRequest;
import cartagows.wsframework.core.ServiceConfiguration;
import cartagows.wsframework.wsaddressing.WSAddressingMessageElem;
import cartagows.wsframework.wsaddressing.WSAddressingParam;
import cartagows.wsframework.wsatomictransaction.ATCoordinator;
import cartagows.wsframework.wsatomictransaction.ATCreateActivityResultContent;
import cartagows.wsframework.wsatomictransaction.ATRegisterPartecipantContent;
import cartagows.wsframework.wsatomictransaction.WSAtomicTransactionParam;

public class WSCoordinationWorker extends WSAgent{
	
	private ArtifactId requestMediator;

	public WSCoordinationWorker(String agentName) throws Exception{
		//, String workspaceName, String workspaceHost) throws CartagoException{
		super(agentName);
		try {
			requestMediator = makeArtifact(WSCoordinationParam.REQUEST_MEDIATOR_DEF_NAME ,
					CartagowsParam.REQUEST_MEDIATOR_CLASS_PATH);
		}
		catch (CartagoException ex) {
			requestMediator = lookupArtifact(WSCoordinationParam.REQUEST_MEDIATOR_DEF_NAME);
		}
	}

	public void run(){
		while(true){

			try {
				
				String recipientWSDLURI,requestID;
				ArtifactId wsProxy;
				int operationID;
				WSMsgInfo res;
				OMElement body;
				OpFeedbackParam<IRequest> requestFeedback = new OpFeedbackParam<IRequest>();
				IRequest request;
				
				//Retrieval of a new WS-Coordination request
				log("[WSCOORD-WORKER:]Waiting for WS-Coordination requests");
				doAction(requestMediator, new Op("getNextRequest", requestFeedback));

				log("[WSCOORD-WORKER:]New request found");
				request = requestFeedback.get();
				requestID = request.getRequestID();
				
				//Retrieving the WSDLURI of the recipient service
				recipientWSDLURI = request.getRecipientWSDLURI();
				
				//Retrieving the identifier of the operation from the request
				operationID = request.getOperationInfo().getOperationID();

				//Switch that manages all the supported operation for the WS-Coordination
				//specification

				switch(operationID){

					/*
					 * The sender is requesting the creation of a new AT coordination activity.
					 * The worker will try to:
					 *  - Create a new coordination context
					 *  - Register the requester as a participant of the new coordination activity
					 *    created.
					 *  - Return the coordination context to the sender agent
					 */

					case WSCoordinationOperation.REQUEST_NEW_COORDINATION_ACTIVITY_AT:
	
						log("[WSCOORD-WORKER:]Creation of a new AT acivity operation");
						if(request.getRequestData().getData() instanceof String){
	
							String partecipantCoordProtocol = (String) request.getRequestData().getData();
	
							//Check of the participant coordination protocol
							if(partecipantCoordProtocol.equals(WSAtomicTransactionParam.VOLATILE_2PC) || 
									partecipantCoordProtocol.equals(WSAtomicTransactionParam.DURABLE_2PC)){

								//The sender of the request is asking to create a new coordination activity with the framework internal
								//coordination service
								if(recipientWSDLURI.equals(WSCoordinationParam.WSDL_URI)){
		
									//recipientWSDLURI = new java.io.File(WSCoordinationParam.WSDL_URI).get.getCanonicalPath();
									/*
									 * Check if the services and the artifacts relates to WSCoordination 
									 * has already been spawned or not 
									 */
									try{
										lookupArtifact(WSCoordinationParam.ACTIVITIES_REGISTRY_DEF_NAME);
									}
									catch (CartagoException ex) 
									{
										//Creation of the artifact that will be used for the managing of the activity
										makeArtifact(WSCoordinationParam.ACTIVITIES_REGISTRY_DEF_NAME, 
												WSCoordinationParam.ACTIVITIES_REGISTRY_CLASSPATH);
										//Creation of the artifact that will be used for the dispatch of the AT messages
										makeArtifact(WSCoordinationParam.ACTIVITIES_MSG_WAREHOUSE_DEF_NAME, 
												WSCoordinationParam.ACTIVITIES_MSG_WAREHOUSE_CLASSPATH);
										//ActivationService and RegistrationService spawn 
										ActivationService actServ = new ActivationService("Agent_"+WSCoordinationParam.ACTIVATION_SERVICE_NAME);
										RegistrationService regServ = new RegistrationService("Agent_"+WSCoordinationParam.REGISTRATION_SERVICE_NAME);
										ATCoordinator mainCoord = new ATCoordinator("Agent_"+WSAtomicTransactionParam.COORDINATOR_SERVICE_NAME);
										actServ.start();
										regServ.start();
										mainCoord.start();
									}
								}
								
								wsProxy = createWSInterface(WSCoordinationParam.WSINTERFACE_DEF_NAME, WSCoordinationParam.WSDL_URI, 
										request.getRecipientServiceName(), request.getRecipientServicePort());
	
								//Request the creation of the coordination context to the internal Activation Service 
								res = doRequestResponse(wsProxy, WSCoordinationWSDLOperation.CREATE_CONTEXT, 
										createCoordinationContextMessage(WSAtomicTransactionParam.AT).toString(), CartagowsParam.LONG_TIMEOUT);
	
								//Inspect of the response message
								body = XMLib.getInstance().buildElementFromString(res.getBody());
								OMElement coordContext = XMLib.getInstance().getElement(WSCoordinationWSDLOperation.CREATE_CONTEXT_RESPONSE, 
										WSCoordinationMessageElem.COORDINATION_CONTEXT, body);
								String registrationServiceEPR = XMLib.getInstance().getElementValue(WSCoordinationMessageElem.REGISTRATION_SERVICE, 
										WSAddressingMessageElem.ADDRESS, coordContext);
	
								//Error: bad response message
								if(registrationServiceEPR==null || coordContext==null){
									log("[WSCOORD-WORKER:]"+ WSCoordinationErrorParam.ERR_BAD_RESPONSE +" from the ActivationService");
									notifyRequestCompletionToMediator(
											new DataContainer(DataContainer.STRING, WSCoordinationErrorParam.ERR_BAD_RESPONSE),
											requestID, false);
								}
								
								//Creation of the activity succeeded
								else{
									log("[WSCOORD-WORKER:]Coordination context created succesfully by the ActivationService");									
									//Request the registration of the sender of the request as a participant for the new coordination activity
									String coordEPR = 
										this.registerPartecipant(coordContext, request.getRequestorEPR(), partecipantCoordProtocol);
									
									//Error: bad response message
									if(coordEPR==null){
										log("[WSCOORD-WORKER:]"+ WSCoordinationErrorParam.ERR_BAD_RESPONSE +" from the RegistrationService");
										notifyRequestCompletionToMediator(
												new DataContainer(DataContainer.STRING,WSCoordinationErrorParam.ERR_BAD_RESPONSE),
												requestID, false);
									}
	
									//Request handling done successfully. The framework has created a new coordination activity and registered
									//the EPR specified in the request as a participant for the new activity.
									else{
										log("[WSCOORD-WORKER:]Partecipant registered. Request handling done successfully");
										//The worker notify to the WSCoordinationRequestMediator the correct handling of the request
										notifyRequestCompletionToMediator(
												new DataContainer(DataContainer.OPERATION_SPECIFIC,
														new ATCreateActivityResultContent(coordEPR, coordContext)),
														requestID, true);
									}
								}
								//Removal of the WSInterfaced used
								//disposeArtifact(wsProxy);
							}
							//Error: Wrong participant coordination protocol
							else{
								log("[WSCOORD-WORKER:]"+WSCoordinationErrorParam.ERR_WRONG_COORD_TYPE);
								notifyRequestCompletionToMediator(
										new DataContainer(DataContainer.STRING, WSCoordinationErrorParam.ERR_WRONG_COORD_TYPE),
										requestID , false);
							}
						}
						//Error: Wrong data type
						else{
							log("[WSCOORD-WORKER:]"+WSCoordinationErrorParam.ERR_WRONG_DATA_TYPE);
							notifyRequestCompletionToMediator(
									new DataContainer(DataContainer.STRING, WSCoordinationErrorParam.ERR_WRONG_DATA_TYPE),
									requestID , false);
						}
	
					break;
	
					case WSCoordinationOperation.REQUEST_NEW_COORDINATION_ACTIVITY_BA:

					break;
	
					case WSCoordinationOperation.REGISTER_PARTICIPANT:
						
						/*
						 * The sender of the request message is asking to be registered as a participant of a coordination
						 * activity that already exists.
						 */
						if(request.getRequestData().getData() instanceof ATRegisterPartecipantContent){
							
							ATRegisterPartecipantContent content = (ATRegisterPartecipantContent) request.getRequestData().getData();
							
							//Request the registration of the sender of the request as a participant for specified coordination activity
							String coordEPR = registerPartecipant(content.getCoordContext(), request.getRequestorEPR(), content.getPartecipantProtocol());
							
							//Error: Bad response message
							if(coordEPR==null){
								log("[WSCOORD-WORKER:]"+ WSCoordinationErrorParam.ERR_BAD_RESPONSE +" from the RegistrationService");
								notifyRequestCompletionToMediator(
										new DataContainer(DataContainer.STRING, WSCoordinationErrorParam.ERR_BAD_RESPONSE),
										requestID, false);
							}
							
							//Request handling done successfully. The framework has registered the EPR specified in the request as a 
							//participant for the activity specified by the coordination context given in input
							else{
								//The worker notify to the WSCoordinationRequestMediator the correct handling of the request
								log("[WSCOORD-WORKER:]Partecipant registered. Request handling done successfully");
								notifyRequestCompletionToMediator(new DataContainer(DataContainer.STRING, coordEPR), requestID, true);
							}

						}
						//Error: Wrong data type
						else{
							log("[WSCOORD-WORKER:]"+WSCoordinationErrorParam.ERR_WRONG_DATA_TYPE);
							notifyRequestCompletionToMediator(new DataContainer(DataContainer.STRING, WSCoordinationErrorParam.ERR_WRONG_DATA_TYPE), requestID, false);
						}
					break;
						
					default:
					break;
				}
			}
			catch(OpRequestTimeoutException ex){
				log("[WSCOORD-WORKER:]No request found for WS-Coordination");
			}
			catch (Exception ex2) 
			{
				log(ex2.toString() + " " +ex2.getCause() + ex2.getLocalizedMessage() + " " + ex2.getMessage());
				ex2.printStackTrace(); 
				return;
			}
		}
	}
	
	private  OMElement createCoordinationContextMessage(String coordProtocol) {
		
		OMElement msg = XMLib.getInstance().buildElement(WSCoordinationMessageElem.CREATE_CONTEXT);
		OMElement coordType = XMLib.getInstance().buildElement(WSCoordinationMessageElem.COORDINATION_TYPE, coordProtocol);
		XMLib.getInstance().addChildNode(msg, coordType);
	    return msg;
	}

	
	private OMElement createRegisterMessage(String coordProtocol, String partecipantEpr){

		OMElement msg = XMLib.getInstance().buildElement("Register");
	    OMElement protocol = XMLib.getInstance().buildElement("ProtocolIdentifier", coordProtocol);
	    OMElement address = XMLib.getInstance().buildElement("Address", partecipantEpr, WSAddressingParam.NAMESPACE, WSAddressingParam.NAMESPACE_PREFIX);
	    OMElement partecipantService = XMLib.getInstance().buildElement(WSCoordinationMessageElem.PARTICIPANT_PROTOCOL_SERVICE);
	    
	    XMLib.getInstance().addChildNode(partecipantService, address);
	    XMLib.getInstance().addChildNode(msg, protocol);
	    XMLib.getInstance().addChildNode(msg, partecipantService);
	    
	    return msg;
	}
	
	private String registerPartecipant(OMElement coordContext, String partecipantEPR, String coordProtocol){
		
		String coordEPR = null;

		try {

			String registrationServiceEPR = XMLib.getInstance().getElementValue(WSCoordinationMessageElem.REGISTRATION_SERVICE, 
					WSAddressingMessageElem.ADDRESS, coordContext);
			ArtifactId wsProxy = createWSInterface(WSCoordinationParam.WSINTERFACE_DEF_NAME, 
					WSCoordinationParam.WSDL_URI,WSCoordinationParam.REGISTRATION_PORT_TYPE, new EndpointReference(registrationServiceEPR));

			//Configuration of the WSInterface with the CoordinationContext given in input
			doAction(wsProxy, new Op("configure", new ServiceConfiguration(coordContext)));

			OMElement registerMsg = createRegisterMessage(coordProtocol, partecipantEPR);

			//Participant registration
			WSMsgInfo res = doRequestResponse(wsProxy,WSCoordinationWSDLOperation.REGISTER, registerMsg.toString(), CartagowsParam.LONG_TIMEOUT);
			OMElement body = XMLib.getInstance().buildElementFromString(res.getBody());
			//Retrieval of the coordEPR 
			coordEPR = XMLib.getInstance().getElementValue(WSCoordinationWSDLOperation.REGISTER_RESPONSE, 
					WSCoordinationMessageElem.COORDINATOR_EPR, WSAddressingMessageElem.ADDRESS ,body);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return coordEPR;
	}
	
	private void notifyRequestCompletionToMediator(DataContainer requestResult, String requestID, boolean requestSucceeded) throws Exception{
		doAction(new Op("notifyRequestCompletion", requestID ,requestResult ,requestSucceeded));
	}
}